/*
 * Created on 20-dic-2004
 */
package guru.merlin.server;

import guru.merlin.bus.MessagingInfrastructureException;
import guru.merlin.core.ConfigurationCantHandException;
import guru.merlin.core.CantHandException;
import guru.merlin.core.NodeConnectionSentry;
import guru.merlin.core.PeerCantHandException;

import guru.merlin.bus.EnvelopeBus;
import guru.merlin.core.Envelope;
import guru.merlin.core.EnvelopeCantHandException;
import guru.merlin.core.EnvelopeHandler;
import guru.merlin.core.EnvelopeProcessor;
import guru.merlin.core.InfrastructureCantHandException;
import guru.merlin.core.Route;
import guru.merlin.core.Router;
import guru.merlin.core.RuntimeConfigurationCantHandException;

import org.apache.log4j.Logger;


/**
 * @author cobach Antigua clase PostRouter, modificada por santiago ventura el 22/07/2005. cambiar
 *         nombre a EPContainer
 */
public class EPContainer implements EnvelopeHandler{
    public boolean started=false;
    public EnvelopeProcessor ep;
    static Logger logger = Logger.getLogger (EPContainer.class);
    java.util.Calendar calendarControl = null;
    java.util.Calendar calendarTmp = null;
    int minControl;
    int minTmp;
    Router router;
    public Route route;
    EnvelopeBus bus;
    boolean trySentry;
    public int attemptsToReconect = 0;
    public int maxAttemptsReconect = 0;
    public long microCycle;
    public String parametersFilePath; //Aadido por Santiago //MOD SANTIAGO
    
    private NodeConnectionSentry ncs;
    public int monitoringRound; //ronda de vigilancia
    
    private EPServer server;
    
    /**
     * Efectua el preprocesamiento, procesamiento y postprocesamiento de objetos envelopes.
     * Implementa la nueva arquitectura de merlin para el control de transacciones
     *
     *
     * La ejecucion del EnvelopeProcessor puede tener los siguientes resultados con sus respectivas
     * "interpretacion" y "reaccion" por parte del Container:
     *
     * <ul>
     * <li>
     * <b>Exito</b>: El Envelope fue procesado con exito; el Container continua el procesamiento.
     * </li>
     * <li>
     * <b>EnvelopeCantHandException</b>: El Envelope no podrá ser procesado por alguna característica propia
     * del mismo; el Container envía el Mensaje a la cola de error
     * </li>
     * <li>
     * <b>PeerCantHandException</b>: El EP tiene problemas en la comunicacion con su <i>Peer</i>; el Container
     * reencola el Envelope e intenta la reconexion del nodo.
     * </li>
     * <li>
     * <b>InfrastructureCantHandException</b>: El EP tiene problemas de operacion debido a su
     * <i>Infraestructura</i>; el Container reencola el Envelope y dormirá un tiempo (milisegundos)
     * esperando que el problema se resuelva.
     *
     * </li>
     * <li>
     * <b>ConfigurationCantHandException</b>: La configuracion actual del EP no permite procesar mensajes;
     * el Container reencola el Envelope y parará el Nodo para que no continue el procesamiento de
     * Envelopes.
     * </li>
     * <li>
     * <b>RuntimeConfigurationCantHandException</b>: La configuracion en Runtime del EP en este momento no
     * le permite procesar el Envelope; el Container envía el Mensaje a la cola de error.
     * </li>
     * </ul>
     *
     *
     *
     *
     *
     *
     *
     *
     *
     * @see guru.merlin.core.EnvelopeHandler#handEnvelope(guru.merlin.core.Envelope)
     */
    public Object handEnvelope (Envelope envelope) {
        Object preProcesingActionReference = null;
        Object procesingActionReference = null;
        Object postProcesingActionReference = null;
        
        try {
            
            //Preprocesamiento
            try {
                preProcesingActionReference = router.preProcesingAction (envelope);
            } catch(EnvelopeCantHandException e){
                router.handleError (envelope, e, e.getMessage () );
                throw e;
            }catch(InfrastructureCantHandException e){
                bus.handEnvelope (envelope);
                throw e;
            }
            
            
            //Procesamiento
            try {
                procesingActionReference = ep.handEnvelope (envelope);
            } catch(EnvelopeCantHandException e){
                //rollback:
                router.preProcesingActionCompensator (preProcesingActionReference);
                
                router.handleError (envelope, e, e.getMessage () );
                
                throw e;
            } catch (PeerCantHandException e) {
                //rollback:
                router.preProcesingActionCompensator (preProcesingActionReference);
                
                //dormir un rato:
                synchronized(this){
                    Thread.sleep (microCycle);
                }
                
                //reconectar:
                try {
                    ep.closeConnection ();
                } catch (Exception e2) {
                    logger.warn ("can't close connectio; will try to open as next step: "+e2);
                }
                try {
                    logger.info (this.route.toString ()+" try to reconnect");
                    ep.openConnection (parametersFilePath);
                    ep.start ();
                } catch (Throwable t) {
                    logger.error (t);
                } finally {
                    bus.handEnvelope (envelope);
                    throw e;
                }
            } catch(InfrastructureCantHandException e){
                //rollback:
                router.preProcesingActionCompensator (preProcesingActionReference);
                
                bus.handEnvelope (envelope);
                
                throw e;
            } catch (ConfigurationCantHandException e) {
                //rollback:
                router.preProcesingActionCompensator (preProcesingActionReference);
                server.metaStop (this);
                logger.warn ("The node reported a configuration problem. Stoped by the container.",e);
                
                bus.handEnvelope (envelope);
                
                throw e;
            } catch(RuntimeConfigurationCantHandException e){
                //rollback:
                router.preProcesingActionCompensator (preProcesingActionReference);
                
                router.handleError (envelope, e, e.getMessage ());
                
                throw e;
            }
            
            
            //Postprocesamiento
            try {
                postProcesingActionReference = router.postProcesingAction (envelope);
            } catch (Throwable t) {
                router.preProcesingActionCompensator (preProcesingActionReference);
                ep.retrieveEnvelope (procesingActionReference); //Compensamos la accin handEnvelope
                logger.error (t);
                throw t;
            }
            
            try {
                //AQUI VA CUALQUIER COSA DESPUES DEL POSTPROCESING ACTION.
            } catch (Exception e) {
                router.preProcesingActionCompensator (preProcesingActionReference);
                ep.retrieveEnvelope (procesingActionReference); //Compensamos la accin handEnvelope
                router.postProcesingActionCompensator (postProcesingActionReference);
                logger.error (e);
                throw e;
            }
            
            //AQUI VAN TODOS LOS COMMITS DE SEGUNDA FASE.
            router.commitPreProcesingAction (preProcesingActionReference);
            ep.commitHandEnvelope (procesingActionReference);
            router.commitPostProcesingAction (postProcesingActionReference);
            
            
        } catch (Throwable t) {
            if (envelope != null) {
                StringBuffer sb = new StringBuffer ();
                StackTraceElement stt[] = t.getStackTrace ();
                sb.append ("Se genero un: " + t + "\n");
                sb.append ("Stacktrace:\n");
                for (int d = 0; d < stt.length; d++) {
                    sb.append (stt[d].toString () + "\n");
                }
                logger.error (sb.toString ());
            } else {
                logger.error ("Envelope was null, eliminated of the queue.");
            }
            t.printStackTrace ();
        } finally{
            callGarbageCollector ();
        }
        
        return envelope; //OJO: que hace esto?
    }
    
    //#######################################################################
    
    
    public void start () throws Exception{ //MOD SANTIAGO
        try {
            ep.start ();
            bus.start (this.route);
            started = true;
            if ( trySentry && (ncs==null || (!ncs.isRunning ())) ) {
                this.startNodeConnectionSentry ();
            }
        }catch (Exception e){
            throw new Exception ("Can't start: "+e);
        }
    }
    
    public void stop () throws Exception{
        try {
            ep.stop ();
            bus.stop (this.route);
            started = false;
        }catch (Exception e){
            throw new Exception ("Can't stop: "+e);
        }
    }
    
    public EPContainer (EPServer server, EnvelopeProcessor h, Router r, Route route_, EnvelopeBus bus, String parametersFilePath, int maxAttemptsReconect, long microCycle, int monitoringRound, boolean trySentry) { //MOD SANTIAGO
        this.server=server;
        this.ep = h;
        router = r;
        this.route = route_;
        this.bus = bus;
        this.parametersFilePath = parametersFilePath;
        this.maxAttemptsReconect = maxAttemptsReconect;
        this.microCycle = microCycle;
        this.monitoringRound = monitoringRound;
        this.trySentry = trySentry;
    }
    
    /**
     *
     * @see guru.merlin.core.EnvelopeHandler#retrieveEnvelope(java.lang.Object)
     */
    public void retrieveEnvelope (Object EnvelopeReference) throws CantHandException {
    }
    
    /**
     *
     * @see guru.merlin.core.EnvelopeHandler#commitHandEnvelope(java.lang.Object)
     */
    public void commitHandEnvelope (Object EnvelopeReference) throws CantHandException {
        //Aqui deberían estar los "commit" que actualmente están en el handEnvelope.
        //Para cambiarlos de lugar se requiere asegurar que el Bus tambien sea transaccional y
        //asegurar la cadena completa.
    }
    
    public void callGarbageCollector (){
        if (calendarControl==null){
            calendarControl = java.util.Calendar.getInstance ();
            minControl= calendarControl.get (java.util.Calendar.MINUTE);
        } else {
            calendarTmp = java.util.Calendar.getInstance ();
            minTmp = calendarTmp.get (java.util.Calendar.MINUTE);
        }
        
        if (calendarTmp!=null&&calendarControl!=null){
            if ((minTmp-minControl)>(1)) {
                calendarControl=null;
                System.gc ();
            }
        }
    }
    
    private void startNodeConnectionSentry (){//MOD SANTIAGO
        try {
            ncs = new NodeConnectionSentry (this);
            ncs.start ();
        } catch (Exception e){
            e.printStackTrace ();
            logger.info ("",e);
        }
    }
    
    private void printStackTraceByLogger (Throwable e){
        StringBuffer sb = new StringBuffer ();
        StackTraceElement stt[] = e.getStackTrace ();
        sb.append ("Se genero un: " + e + "\n");
        sb.append ("Stacktrace:\n");
        for (int d = 0; d < stt.length; d++) {
            sb.append (stt[d].toString () + "\n");
        }
        logger.error (sb.toString ());
    }
}